﻿using System.Reflection;
using System.Web;
using NHibernate;
using NHibernate.Cfg;

namespace VictorBlaga.DocumentManagement.Infrastructure.DataAccess
{
    public sealed class NHibernateHelper
    {
        private const string CurrentSessionKey = "nhibernate.current_session";
        private const string CurrentTransactionKey = "nhibernate.current_transaction";
        //private const string InfrastructureAssembleyName = "VictorBlaga.DocumentManagement.Infrastructure";
        private readonly  ISessionFactory _sessionFactory;

        private ISession _session;
        private ITransaction _transaction;

        public static NHibernateHelper Instance
        {
            get
            {
                return Nested.Instance;
            }
        }

        private class Nested
        {
            static Nested() {}
            public static readonly NHibernateHelper Instance = new NHibernateHelper();
            
        }

        private NHibernateHelper()
        {
            var cfg = new Configuration();
            cfg.Configure();
            cfg.AddAssembly(Assembly.GetExecutingAssembly());
            _sessionFactory = cfg.BuildSessionFactory();
        }

        private ISession ContextSession
        {
            get
            {
                if (HttpContext.Current != null)
                    return HttpContext.Current.Items[CurrentSessionKey] as ISession;
                return _session;
            }
            set
            {
                if (HttpContext.Current != null)
                    HttpContext.Current.Items[CurrentSessionKey] = value;
                else
                    _session = value;
            }
        }

        private ITransaction ContextTransaction
        {
            get {
                if (HttpContext.Current != null)
                    return HttpContext.Current.Items[CurrentTransactionKey] as ITransaction;
                return _transaction;
            }
            set
            {
                if (HttpContext.Current != null) HttpContext.Current.Items[CurrentTransactionKey] = value;
                else
                    _transaction = value;
            }
        }

        public ISession BeginSession()
        {
            ISession session = ContextSession;

            if (session == null)
            {
                session = _sessionFactory.OpenSession();
                ContextSession = session;
            }

            return session;
        }

        public void CloseSession()
        {
            if (ContextSession == null)
            {
                return;
            }

            ContextSession.Close();
            ContextSession = null;
        }

        public void BeginTransaction()
        {
            ITransaction transaction = ContextTransaction;

            if (transaction == null)
            {
                transaction = BeginSession().BeginTransaction();
                ContextTransaction = transaction;
            }
        }

        public ITransaction GetTransaction()
        {
            ITransaction transaction = ContextTransaction;
            if (transaction == null)
            {
                BeginTransaction();
                transaction = ContextTransaction;
            }

            return transaction;
        }

        public void CommitTransaction()
        {
            ITransaction transaction = ContextTransaction;

            try
            {
                if (TransactionIsOpened)
                {
                    transaction.Commit();
                    ContextTransaction = null;
                }
            }
            catch (HibernateException)
            {
                RollbackTransaction();
                throw;
            }
        }

        public void RollbackTransaction()
        {
            ITransaction transaction = ContextTransaction;

            try
            {
                if (TransactionIsOpened)
                {
                    transaction.Rollback();
                }

                ContextTransaction = null;
            }
            finally
            {
                CloseSession();
            }
        }

        private bool TransactionIsOpened
        {
            get
            {
                bool result = true;
                ITransaction transaction = ContextTransaction;
                if (transaction == null || transaction.WasCommitted || transaction.WasRolledBack) result = false;
                return result;
            }
        }

        public void CloseSessionFactory()
        {
            if (_sessionFactory != null)
            {
                _sessionFactory.Close();
            }
        }
    }
}
